﻿
package extremefx.ui {
	import extremefx.drawing.Color;
	import extremefx.drawing.SlicedBitmap;
	import extremefx.errors.NotImplementedError;
	import extremefx.events.EventHandler;
	import extremefx.extensions.isNullOrEmpty;
	import extremefx.geom.Edge;
	import extremefx.modules.css.DomNodeKinds;
	import extremefx.modules.css.IDomNode;
	import extremefx.tools.getFunctionByName;
	import extremefx.ui.Component;
	import extremefx.ui.styles.UIStates;
	import extremefx.ui.styles.UIStyle;

	import flash.display.Sprite;
	import flash.utils.Dictionary;

	/**
	 * @author Marcelo Volmaro
	 */
	public class UIComponent extends Component implements IDomNode {
		private static var _selected:UIComponent;
		private static const _get:Function = getFunctionByName("extremefx.i18n.Resources", "get");
		
		private var _focusRec:SlicedBitmap;
		private var _border:SlicedBitmap;
		private var _background:SlicedBitmap;
		
		protected var _mSprite:Sprite;

		private var _attrs:Dictionary;
		private var _parentComponent:UIComponent;
		private var _keysHooked:Boolean;
//		private var _mouseHooked:Boolean;
		private var _enableKeys:Boolean;
		
		protected var _enabled:Boolean;
		protected var _style:UIStyle;
		protected var _focused:Boolean;
		protected var _focusable:Boolean;
		protected var _tabfocused:Boolean;
		protected var _pressOnce:Boolean;
		protected var _focusVisual:Boolean;
		
		public function UIComponent() {
			super();
			
			_attrs = new Dictionary();
			_tabfocused = false;
			_focusable = _enableKeys = _enabled = _focusVisual = _tabEnabled = true;
			
			_mSprite = new Sprite();
			_visualUI.addChild(_mSprite);
		}
		
		public function get computedStyle():UIStyle {
			return _style;
		}
		
		protected function initStyle(pType:String, pParameters:UIParameters):UIParameters {
			_kind = pType;
			
			if (pParameters){
				if (!isNullOrEmpty(pParameters._id)) {
					_attrs["id"] = _name = pParameters._id;
				}
				
				if (!isNullOrEmpty(pParameters._className)) {
					_attrs["class"] = pParameters.className;
				}
					
				_parentComponent = pParameters._parent;
				
			} else {
				pParameters = new UIParameters();
			}
			
			_style = StyleManager.getStyleFor(this);
			return pParameters;
		}

		public function hasFocus():Boolean {
			return _focused;
		}

		public function isFocusable():Boolean {
			return _focusable;
		}
		
		protected function _getText(pRes:String):String {
			if (pRes.charAt() != "@") return pRes; 
			if (_get == null) return pRes;
			return _get(pRes.substr(1));
		}
				
		internal function isTabEnabled():Boolean {
			return _enabled && _tabEnabled;
		}
		
		internal function focusable(pFocusable:Boolean):void {
			_focusable = pFocusable;
			if (!pFocusable) tabEnabled(false);
		}
		
		//abstract
		internal var _componentSelect:Function;
		
		internal function _isSelected():Boolean {
			return _selected == this;
		}

		public function focus():void {
			if (_selected == this) return;

			if (_componentSelect is Function) {
				_componentSelect();
			}

			if (_focusable){
				if (_focused) return;
				
				_focused = true;
				if (_oldFocused && _oldFocused != this){
					_oldFocused.blur();
				}
				
				_oldFocused = this;
				if (_parent && _parent._containerTabGroup) _parent._containerTabGroup.focus(this);
				
				if (_eOnFocus) _eOnFocus.fire();

				if (_focusVisual && _visualUI.stage) {
					_visualUI.stage.focus = _visualUI;
				}
			}
			
			_selected = this;
			_switchKeys(true);
		}

		public function blur():void {
			if (_focused){
				if (_visualUI.stage && _visualUI.stage.focus == _visualUI) _visualUI.stage.focus = null;
				_focused = false;
				if (_eOnBlur) _eOnBlur.fire();
			}
			
			_selected = null;
			tabFocus = false;
			
			_switchKeys(false);
			invalidate();
		}
		
		override public function dispose():void {
			if (_disposing) return;
			super.dispose();
			
			_attrs = null;
			blur();
			tabEnabled(false);
			
			if (_background){
				_visualUI.removeChild(_background);
				_background.dispose();
				_background = null;
			}
			
			if (_border){
				_visualUI.removeChild(_border);
				_border.dispose();
				_border = null;
			}

			_visualUI.removeChild(_mSprite);
			
			if (_eOnBlur) _eOnBlur.dispose();
			if (_eOnFocus) _eOnFocus.dispose();
		}
		
		internal function set tabFocus(pFocus:Boolean):void {
			_tabfocused = pFocus;
			
			if (pFocus){
				if (_focusable && !_focusRec){
					var bmp:SlicedBitmap = _style.focusImage;
					if (bmp){
						var bmp2:SlicedBitmap = (_visualUI.addChild(bmp)) as SlicedBitmap;
						var rec:Edge = _style.focusRectangle;
						bmp2.x = rec.left;
						bmp2.y = rec.top;
						bmp2.setSize(rec.getWidth(_width), rec.getHeight(_height));
						_focusRec = bmp2;
					}
				}
				
			} else if (_focusRec){
				if (_visualUI.contains(_focusRec)){
					_visualUI.removeChild(_focusRec);
				}
				
				_focusRec = null;
			}
			
			if (pFocus) focus();
			invalidate();
		}
		
		internal function get tabFocus():Boolean {
			return _tabfocused;
		}
		
		internal function setKeyEnabled(pK:Boolean):void {
			if (pK){
				_switchKeys(_focused);
				
			} else {
				_switchKeys(false);
			}
			
			_enableKeys = pK;
		}

		protected function _switchKeys(pS:Boolean):void {
			if (!_enableKeys) return;
			
			if (pS) {
				if (!_keysHooked){
					_keysHooked = true;
					KeyHandler.onKeyDown.add(_keyDownHandler);
					KeyHandler.onKeyUp.add(_keyUpHandler);
				}

			} else if (_keysHooked){
				_keysHooked = false;
				KeyHandler.onKeyDown.remove(_keyDownHandler);
				KeyHandler.onKeyUp.remove(_keyUpHandler);
			}
		}
		
		//NORMAL, HOVER, ACTIVE
		//
		protected function _getState(pState:uint, pChecked:Boolean = false):uint {
			if (_enabled){
				pState &= ~UIStates.DISABLED;
					
			} else {
				pState |= UIStates.DISABLED;
			}
			
			if (pChecked){
				pState |= UIStates.PRESSED;
				
			} else {
				pState &= ~UIStates.PRESSED;
			}
			
			return pState;
		}
		
		protected function _setDecoState(pState:uint, pWidth:uint, pHeight: uint):void {
			var c:String = _style.getRawProperty("backgroundColor"), color:Color; 
			if (c != null) {
				color = Color.parse(c);
				if (color.alpha != 0) {
					_visualUI.opaqueBackground = color.toRgb();
					_mSprite.opaqueBackground = color.toRgb();
				}
			}
			
			var bmp:SlicedBitmap = _style.getImage(pState, "backgroundImageUrl");
			
			if (bmp){
				if (_background){
					_visualUI.removeChild(_background);
					_background.dispose();
				}
				
				(_background = (_visualUI.addChildAt(bmp, 0)) as SlicedBitmap).setSize(pWidth, pHeight);
			}
			
			bmp = _style.getImage(pState);
			
			if (bmp){
				if (_border){
					_visualUI.removeChild(_border);
					_border.dispose();
				}
				
				(_border = (_visualUI.addChild(bmp) as SlicedBitmap)).setSize(pWidth, pHeight);
			}
		}
		
		public function get enabled():Boolean {
			return _enabled;
		}

		public function set enabled(pEnabled:Boolean):void {
			if (_enabled == pEnabled) return;
			
			_enabled = pEnabled;
			setKeyEnabled(pEnabled);
			if (!pEnabled) blur();
			invalidate();
		}
		
		public function get visible():Boolean {
			return _visualUI.visible;
		}
		
		public function set visible(pVisible:Boolean):void {
			_visualUI.visible = pVisible;
		}
		
		private var _eOnFocus:EventHandler;
		public function get onFocus():EventHandler{
			if (_eOnFocus == null) _eOnFocus = new EventHandler(this);
			return _eOnFocus;
		}
		
		private var _eOnBlur:EventHandler;
		public function get onBlur():EventHandler{
			if (_eOnBlur == null) _eOnBlur = new EventHandler(this);
			return _eOnBlur;
		}
		
		public function get nodeName():String {
			return _kind;
		}
		
		public function get nodeKind():String {
			return DomNodeKinds.ELEMENT;
		}
		
		public function get previousSibling():IDomNode {
			throw new NotImplementedError();
		}
		
		public function get nextSibling():IDomNode {
			throw new NotImplementedError();
		}
		
		public function get parentNode():IDomNode {
			return _parentComponent;
		}
		
		public function get attributes():Dictionary {
			return _attrs;
		}
	}
}
